home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / TURBOPASCAL WIN / OWLDEMOS.PAK / SERVER.PAS < prev    next >
Pascal/Delphi Source File  |  1992-06-08  |  35KB  |  1,005 lines

  1. {***************************************************}
  2. {                                                   }
  3. {   Turbo Pascal for Windows                        }
  4. {   Windows 3.1 OLE Server Demonstration Program    }
  5. {               Server Object Unit                  }
  6. {                                                   }
  7. {   Copyright (c) 1992 by Borland International     }
  8. {                                                   }
  9. {***************************************************}
  10.  
  11. { This unit defines the Server and Document objects, which
  12.   represent the Ole Server and Ole Document, respectively.
  13.   The Server interfaces with the Client application at the
  14.   highest level, managing the creation and manipulation of
  15.   Documents.
  16.  
  17.   Interaction between the Client and these objects is carried
  18.   out through a series of callback functions, which are also
  19.   defined here.
  20.  
  21.   NOTE that we only have one document per server. if yours
  22.   was an MDI app, then you would have a list of documents.
  23.  
  24.   Note: To compile the OLE Server demo, set Compile|Primary File to OLESERVR.PAS
  25. }
  26.  
  27. unit Server;
  28.  
  29. interface
  30.  
  31. uses WinTypes, CommDlg, Ole, WObjects, OleTypes, OleObj;
  32.  
  33. type
  34.  
  35. { The following record types represent the Server and Document
  36.   objects within the OLE library.  They are based on the
  37.   standard structures defined in Ole.pas, and each adds one
  38.   field to provide access back to the TPW object which represents
  39.   it.
  40. }
  41.   POleServerObj = ^TOleServerObj;
  42.  
  43.   PAppServer = ^TAppServer;
  44.   TAppServer = record
  45.     OleServer: TOleServer;
  46.     Owner    : POleServerObj;
  47.   end;
  48.  
  49.   POleDocument  = ^TOleDocument;
  50.  
  51.   PAppServerDoc = ^TAppServerDoc;
  52.   TAppServerDoc = record
  53.     OleServerDoc: TOleServerDoc;
  54.     Owner       : POleDocument;
  55.   end;
  56.  
  57. { TOleServerObj }
  58.  
  59. { This object represents the OLE Server, wrapping useful
  60.   behaviors around the basic TOleServer structure that is
  61.   used within OLE to represent a Server.  This structure
  62.   is represented by the AppServer data field, which is of
  63.   the TAppServer type defined in oleservr.pas, and which
  64.   includes an additional field to point back to Self so
  65.   that our callback functions can reference this object.
  66. }
  67.   TOleServerObj = object(TObject)
  68.     AppServer : TAppServer;
  69.     ServerHdl : LHServer;       { Registration handle returned
  70.                                   by server library}
  71.     Document  : POleDocument;  
  72.     IsReleased: Boolean;        { True if Release method has been called}
  73.  
  74.     constructor Init(App: PApplication; Embedded: Boolean);
  75.     constructor InitFromFile(App: PApplication; Path: PChar);
  76.  
  77.     function Initialize(App: PApplication): Boolean;
  78.  
  79.     function RegisterWithDatabase: Boolean; virtual;
  80.     function WantsToRegister: Boolean; virtual;
  81.   end;
  82.  
  83.  
  84. { TOleDocument }
  85.  
  86. { This object represents the OLE ServerDoc, wrapping useful
  87.   behaviors around the basic TOleServerDoc structure that is
  88.   used within OLE to represent a document.  This structure
  89.   is represented by the AppServerDoc data field, which is of
  90.   the TAppServerDoc type defined in oleservr.pas, and which
  91.   includes an additional field which points back to Self so
  92.   that our callback functions can reference this object.
  93. }
  94.   TOleDocument = object(TObject)
  95.     AppServerDoc: TAppServerDoc;
  96.     ServerDoc   : LHServerDoc;     { Registration handle returned by
  97.                                      server library }
  98.     DocType     : TDocType;
  99.     Name        : PChar;
  100.     OleObject   : POleObjectObj;
  101.     IsDirty     : Boolean;
  102.     IsReleased  : Boolean;  { True if Release method has been called }
  103.  
  104.     constructor Init(Server: POleServerObj; Doc: LHServerDoc;
  105.       Path: PChar; Dirty: Boolean);
  106.  
  107.     procedure Setup(Path: PChar; MaxPathLen: Integer;
  108.       var FNStruct: TOpenFileName); virtual;
  109.     function  LoadFromFile(Path: PChar): Boolean; virtual;
  110.     procedure SaveDoc; virtual;
  111.     procedure SaveAs; virtual;
  112.     procedure Reset(Path: PChar); virtual;         
  113.     procedure SetDocumentName(NewName: PChar;
  114.       ChangeCaption: Boolean); virtual;
  115.     function  PromptForOpenFileName(Path: PChar): Boolean; virtual;
  116.   end;
  117.  
  118. function TOleServerObj_InitVTbl(Inst: THandle): Boolean;
  119. function TOleDocument_InitVTbl(Inst: THandle): Boolean;
  120.  
  121.  
  122. implementation
  123.  
  124. uses Strings, WinProcs, ServrWin, OleApp, ShellAPI;
  125.  
  126. { Global variables }
  127.  
  128. var
  129.   OleServerVTbl   : TOleServerVTbl;
  130.   OleServerDocVTbl: TOleServerDocVTbl;
  131.  
  132.   Filter          : array [0..100] of Char;   { Used in Setup }
  133.   SimpleName      : array [0..13]  of Char;
  134.  
  135. const
  136.   UnnamedDoc: PChar = '(Untitled)';
  137.  
  138.  
  139.  
  140.  
  141. { Server Callback Functions }
  142.  
  143. { The first parameter to each callback is a pointer to the TOleServer
  144.   structure that defines this document.  In each case, we know that it
  145.   will really be a pointer to a TAppServer record, which includes a
  146.   pointer to the Pascal object which owns the TOleServer record.  We
  147.   can therefore use a typecast to access that object, and thus find our
  148.   way back to Self.
  149. }
  150.  
  151. { Handles the Open callback. The user has activated a linked object in an
  152.   OLE client by calling OleActivate.  Similar to CreateFromTemplate in that
  153.   we need to create a document, initialize it with the contents of file 
  154.   'DocName', and save the file name for later use.
  155.  
  156.   WHAT TO DO:
  157.     - Create a TOleDocument of class 'ClassName' (since we only have one
  158.       class we can ignore the class name)
  159.     - Initialize the document with the contents of file 'DocName'
  160.     - Associate handle 'Doc' with the document
  161.     - Store the pointer to the TOleDocument in 'ServerDoc'
  162.     - Save file name 'DocName'
  163.     - Return ole_Ok if successful, ole_Error_Open otherwise
  164. }
  165. function Open(Server: POleServer; Doc: LHServerDoc; DocName: PChar;
  166.               var ServerDoc: POleServerDoc): TOleStatus; export;
  167. var
  168.   SelfPtr: POleServerObj;
  169.   NewDoc : POleDocument;
  170. begin
  171.   SelfPtr := PAppServer(Server)^.Owner;
  172.  
  173.   NewDoc := New(POleDocument, Init(SelfPtr, Doc, DocName, False));
  174.   if NewDoc = nil then
  175.     Open := ole_Error_Edit
  176.   else 
  177.   begin
  178.     ServerDoc := @NewDoc^.AppServerDoc;
  179.     Open      := ole_Ok;
  180.   end;
  181. end;
  182.  
  183. { Handles the Create callback.  Called by the server library when a client
  184.   application has created a new embedded object by calling OleCreate.
  185.  
  186.   WHAT TO DO:
  187.     - Create an *untitled* TOleDocument of class 'ClassName' (since we
  188.       only have one class we can ignore the class name) and mark it as dirty
  189.     - Associate handle 'Doc' with the document
  190.     - Store the pointer to the TOleDocument in 'ServerDoc'
  191.     - Return ole_Ok if successful, ole_Error_New otherwise
  192.  
  193.   If your app is an MDI application then you would also allocate a window
  194.   here, but since this app isn't the window is already created.
  195.  
  196.   'DocName' is the name of the document as it appears in the client
  197.   class. DON'T use this to change the title bar, use what you get when
  198.   the document is sent the message 'SetHostNames'.
  199.  
  200.   NOTE: Since we only have one document we could have created it during
  201.         initialization
  202. }
  203. function Create(Server: POleServer; Doc: LHServerDoc;
  204.                 Class, DocName: PChar;
  205.                 var ServerDoc: POleServerDoc): TOleStatus; export;
  206. var
  207.   SelfPtr: POleServerObj;
  208.   NewDoc : POleDocument;
  209. begin
  210.   SelfPtr:= PAppServer(Server)^.Owner;
  211.  
  212.   NewDoc := New(POleDocument, Init(SelfPtr, Doc, nil, True));
  213.   if NewDoc = nil then
  214.     Create := ole_Error_New
  215.   else 
  216.   begin
  217.     ServerDoc := @NewDoc^.AppServerDoc;
  218.     PServerWindow(Application^.MainWindow)^.BeginEmbedding;
  219.     Create := ole_Ok;
  220.   end;
  221. end;
  222.  
  223. { Handles the CreateFromTemplate callback.  Called by the server library 
  224.   when a client application has created a new linked object specifying a 
  225.   template by calling OleCreateFromTemplate. What this really means is that
  226.   we need to create a document and initialize it with the contents of a file.
  227.   'DocName' is the name of the document as it appears in the client class.
  228.   DON'T use this to change the title bar, use what you get when the document
  229.   is sent message 'SetHostNames'
  230.  
  231.   WHAT TO DO:
  232.     - Create a TOleDocument of class 'ClassName' (since we only have one
  233.       class we can ignore the class name)
  234.     - Initialize the document with the contents of file 'TemplateName'
  235.     - Associate handle 'Doc' with the document
  236.     - Store the pointer to the TOleDocument in 'ServerDoc'
  237.     - Return ole_Ok if successful, ole_Error_Template otherwise
  238.  
  239.     If your app is an MDI application then you would also allocate a window
  240.     here, but since this app isn't the window is already created.
  241.  
  242.     NOTE: since we only have one document we could have created it during
  243.           initialization
  244. }
  245. function CreateFromTemplate(Server: POleServer; Doc: LHServerDoc;
  246.   Class, DocName, TemplateName: PChar;
  247.   var ServerDoc: POleServerDoc): TOleStatus; export;
  248. var
  249.   SelfPtr: POleServerObj;
  250.   NewDoc : POleDocument;
  251. begin
  252.   SelfPtr:= PAppServer(Server)^.Owner;
  253.  
  254.   NewDoc := New(POleDocument, Init(SelfPtr, Doc, TemplateName, False));
  255.   if NewDoc = nil then
  256.     CreateFromTemplate := ole_Error_New
  257.   else 
  258.   begin
  259.     ServerDoc := @NewDoc^.AppServerDoc;
  260.     PServerWindow(Application^.MainWindow)^.BeginEmbedding;
  261.     CreateFromTemplate := ole_Ok;
  262.   end
  263. end;
  264.  
  265. { Handles the Edit callback.  Called by the server library when a client
  266.   application has activated an embedded object for editing.  This is exactly
  267.   like 'Create' except that the document will receive a 'GetData' message to
  268.   create the object, and the object will receive a 'SetData' message to 
  269.   initialize itself
  270.  
  271.   'DocName' is the name of the document as it appears in the client class.
  272.   DON'T use this to change the title bar, use what you get when the document
  273.   is sent message 'SetHostNames'
  274.  
  275.   WHAT TO DO:
  276.     - Create a TOleDocument of class 'ClassName' (since we only have one
  277.       class we can ignore the class name)
  278.     - Associate handle 'Doc' with the document
  279.     - Store the pointer to the TOleDocument in 'ServerDoc'
  280.     - Return ole_Ok if successful, ole_Error_Edit otherwise
  281. function Edit(Server: POleServer; Doc: LHServerDoc; Class, DocName: PChar;
  282.   var ServerDoc: POleServerDoc): TOleStatus; export;
  283. var
  284.   SelfPtr: POleServerObj;
  285.   NewDoc : POleDocument;
  286. begin
  287.   SelfPtr:= PAppServer(Server)^.Owner;
  288.   NewDoc := New(POleDocument, Init(SelfPtr, Doc, nil, False));
  289.   if NewDoc = nil then
  290.     Edit := ole_Error_Edit
  291.   else 
  292.   begin
  293.     ServerDoc := @NewDoc^.AppServerDoc;
  294.     PServerWindow(Application^.MainWindow)^.BeginEmbedding;
  295.     Edit := ole_Ok;
  296.   end;
  297. end;
  298.  
  299. { Handles the Exit callback.  We have been instructed by the library to 
  300.   exit immediately because of a fatal error.
  301.  
  302.   WHAT TO DO:
  303.     - Hide the window to prevent user interaction
  304.     - Call OleRevokeServer and ignore a return of ole_Wait_For_Release
  305.     - Terminate the application immediately
  306.     - Return ole_Ok if successful, ole_Error_Generic otherwise
  307. }
  308. function Exit(Server: POleServer): TOleStatus; export;
  309. var
  310.   SelfPtr: POleServerObj;
  311. begin
  312.   SelfPtr := PAppServer(Server)^.Owner;
  313.  
  314.   Application^.MainWindow^.Show(sw_Hide);
  315.  
  316.   OleRevokeServer(SelfPtr^.ServerHdl);
  317.  
  318.   PostAppMessage(GetCurrentTask, wm_Quit, 0, 0);
  319.   Exit := ole_Ok;
  320. end;
  321.  
  322. { Handles the Release callback.  This routine gets called by the server
  323.   library after the server has called OleRevokeServer and when the DDE 
  324.   conversation with the client has been successfully closed.  This tells
  325.   us that there are no connections to the server, its documents, or their
  326.   objects and that we are free to terminate.
  327.  
  328.   WHAT TO DO:
  329.     - Set a flag to indicate that 'Release' has been called
  330.     - If the application is hidden and we *haven't* called OleRevokeServer
  331.       then we *must* terminate by posting a wm_Close message
  332.     - Free any resources allocated including documents, but *not* the
  333.       TOleServer structure
  334.     - Return ole_Ok if successful, Ole_Error_Generic otherwise
  335.  
  336.   NOTE: this routine is tricky because it is invoked under different
  337.   circumstances:
  338.     - User brought up the server and then closes it, which causes us
  339.       to call OleRevokeServer which means the server will eventually
  340.       receive a 'Release' message
  341.  
  342.     - The server was started to perform an invisible update for a client
  343.       (i.e. the server has always been hidden). In this case the server will
  344.       receive a 'Release' message and we must tell ourselves to close
  345.       because there is no user interaction.
  346. }
  347. function Release(Server: POleServer): TOleStatus; export;
  348. var
  349.   SelfPtr: POleServerObj;
  350. begin
  351.   SelfPtr := PAppServer(Server)^.Owner;
  352.  
  353.   { If we haven't been sent a 'Release' message yet and our main window is
  354.     hidden then we post a quit message.  NOTE: Call PostMessage and not 
  355.     PostQuitMessage because PostQuitMessage might bypass your application's
  356.     necessary cleanup procedures.
  357.   }
  358.   if (not SelfPtr^.IsReleased) and
  359.       (not IsWindowVisible(Application^.MainWindow^.HWindow)) then
  360.     PostMessage(Application^.MainWindow^.HWindow, wm_Close, 0, 0);
  361.  
  362.   SelfPtr^.IsReleased := True;
  363.  
  364.   Release := ole_Ok;
  365. end;
  366.  
  367. { Handles the Execute callback. If your app supports DDE execution
  368.   commands then you would handle this event. Since we don't we return
  369.   ole_Error_Command.
  370. }
  371. function Execute(Server: POleServer; Commands: THandle): TOleStatus; export;
  372. begin
  373.   Execute := ole_Error_Command;
  374. end;
  375.  
  376.  
  377. { TOleServerObj Methods }
  378.  
  379. { Constructs an untitled instance of the OLE server document.
  380. }
  381. constructor TOleServerObj.Init(App: PApplication; Embedded: Boolean);
  382. begin
  383.   if Initialize(App) and (not Embedded) then
  384.     Document := New(POleDocument, Init(@Self, 0, nil, False));
  385. end;
  386.  
  387. { Constructs an instance of the Server Object, creating an OLE document
  388.   and initializing it from file 'Path'.
  389. }
  390. constructor TOleServerObj.InitFromFile(App: PApplication; Path: PChar);
  391. begin
  392.   if Initialize(App) then
  393.     Document := New(POleDocument, Init(@Self, 0, Path, False));
  394. end;
  395.  
  396. { Completes the construction of Self, attaching Self to the given
  397.   application.  Returns True if successful, False if not.
  398. }
  399. function TOleServerObj.Initialize(App: PApplication): Boolean;
  400. var
  401.   Status: TOleStatus;
  402. begin
  403.   AppServer.OleServer.lpvtbl:= @OleServerVTbl;
  404.   AppServer.Owner           := @Self;
  405.  
  406.   IsReleased := False;
  407.  
  408.   { Attach Self to the containing application.
  409.   }
  410.   POleApp(App)^.Server := @Self;
  411.  
  412.   { Since we can't handle multiple documents (MDI), request that we use
  413.     multiple instances to support multiple objects
  414.   }
  415.   Status := OleRegisterServer(ClassKey, @AppServer, ServerHdl, HInstance,
  416.     ole_Server_Multi);
  417.  
  418.   Initialize := True;
  419.   if Status = ole_Error_Class then
  420.   begin
  421.     if RegisterWithDatabase then
  422.       OleRegisterServer(ClassKey, @AppServer, ServerHdl, HInstance,
  423.         ole_Server_Multi)
  424.     else
  425.       Initialize := False;
  426.   end;
  427. end;
  428.  
  429. { Displays an action message prompting the user to see if they want to
  430.   register Application^.Name with the system registration database.
  431.   Returns True if user says YES and False is users says NO.  If user
  432.   says NO we terminate the app.
  433. }
  434. function TOleServerObj.WantsToRegister: Boolean;
  435. var
  436.   Buf: array [0..255] of Char;
  437. begin
  438.   StrCopy(Buf, Application^.Name);
  439.   StrCat(Buf, ' is not registered as an OLE server in the ' +
  440.     'system registration');
  441.   StrCat(Buf, ' database. Do you want to register it?');
  442.  
  443.   if MessageBox(0, Buf, Application^.Name, mb_YesNo or
  444.       mb_IconQuestion) = idYes then
  445.     WantsToRegister := True
  446.   else 
  447.   begin
  448.     PostAppMessage(GetCurrentTask, wm_Quit, 0, 0);
  449.  
  450.     { We also need to make sure that the main window doesn't get displayed.
  451.       We have a couple of choices: set 'CmdShow' to sw_Hide or set 'Status'
  452.       to non-zero.  Since the user electing not to register isn't really an
  453.       error, let's set 'CmdShow'.
  454.     }
  455.     CmdShow := sw_Hide;
  456.     WantsToRegister := False;
  457.   end;
  458. end;
  459.  
  460. { Registers us as an OLE server with the system registration database.
  461.   This would typically be done during *installation* of the app and not
  462.   when the app runs.
  463.  
  464.   NOTE: We first prompt the user to see if they want us to register. if so
  465.         we register and if not we terminate the app.
  466. }
  467. function TOleServerObj.RegisterWithDatabase: Boolean;
  468. var
  469.   Buf  : array [0..127] of Char;
  470.   Path : array [0..255] of Char;
  471. begin
  472.   if not WantsToRegister then
  473.     RegisterWithDatabase := False
  474.   else 
  475.   begin
  476.     StrCopy(Buf, '.');
  477.     StrCat(Buf, FileExt);
  478.     RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, ClassKey, StrLen(ClassKey));
  479.     RegSetValue(hkey_Classes_Root, ClassKey, Reg_Sz, ClassValue,
  480.       StrLen(ClassValue));
  481.  
  482.     { Register verb actions EDIT and PLAY with EDIT being the primary verb.
  483.     }
  484.     StrCopy(Buf, ClassKey);
  485.     StrCat(Buf, '\protocol\StdFileEditing\verb\0');
  486.     RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, 'Edit', 4);
  487.   
  488.     StrCopy(Buf, ClassKey);
  489.     StrCat(Buf, '\protocol\StdFileEditing\verb\1');
  490.     RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, 'Play', 4);
  491.  
  492.     { Register a full pathname to the executable with the database.
  493.     }
  494.     GetModuleFileName(HInstance, Path, SizeOf(Path));
  495.     StrCopy(Buf, ClassKey);
  496.     StrCat(Buf, '\protocol\StdFileEditing\server');
  497.     RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, Path, StrLen(Path));
  498.   
  499.     { Inform the user that we have registered as an OLE server by displaying
  500.       an information message.
  501.     }
  502.     StrCopy(Buf, Application^.Name);
  503.     StrCat(Buf, ' successfully registered as an OLE server with the system '+
  504.       'registration database.');
  505.   
  506.     MessageBox(0, Buf, Application^.Name, mb_Ok or mb_IconInformation);
  507.     RegisterWithDatabase := True;
  508.   end
  509. end;
  510.  
  511. { Creates the instance thunks for the OleServer callback tables.
  512. }
  513. function TOleServerObj_InitVTbl(Inst: THandle): Boolean;
  514. begin
  515.   @OleServerVTbl.Open    := MakeProcInstance(@Open,    Inst);
  516.   @OleServerVTbl.Create  := MakeProcInstance(@Create,  Inst);
  517.   @OleServerVTbl.CreateFromTemplate
  518.                          := MakeProcInstance(@CreateFromTemplate, Inst);
  519.   @OleServerVTbl.Edit    := MakeProcInstance(@Edit,    Inst);
  520.   @OleServerVTbl.Exit    := MakeProcInstance(@Exit,    Inst);
  521.   @OleServerVTbl.Release := MakeProcInstance(@Release, Inst);
  522.   @OleServerVTbl.Execute := MakeProcInstance(@Execute, Inst);
  523.  
  524.   TOleServerObj_InitVTbl := (@OleServerVTbl.Open <> nil) and
  525.                             (@OleServerVTbl.Create <> nil) and
  526.                             (@OleServerVTbl.CreateFromTemplate <> nil) and
  527.                             (@OleServerVTbl.Edit <> nil) and
  528.                             (@OleServerVTbl.Exit <> nil) and
  529.                             (@OleServerVTbl.Release <> nil) and
  530.                             (@OleServerVTbl.Execute <> nil);
  531. end;
  532.  
  533.  
  534. { Document Callback Functions }
  535.  
  536. { The first parameter to each callback is a pointer to the TOleServerDoc
  537.   structure that defines this document.  In each case, we know that it
  538.   will really be a pointer to a TAppServerDoc record, which includes a
  539.   pointer to the Pascal object which owns the TOleServerDoc record.  We
  540.   can therefore use a typecast to access that object, and thus find our
  541.   way back to Self.
  542. }
  543.  
  544. { Handles the Save callback.  This method is only used when the server is
  545.   editing a linked object: the client application is closing and the user
  546.   has requested saving the client document which contains a linked object.
  547.  
  548.   WHAT TO DO:
  549.     - Save the document to the filename which was passed in when the document
  550.       was opened for linking
  551.     - Return Ole_Ok if successful, ole_Error_Generic otherwise
  552. }
  553. function Save(Doc: POleServerDoc): TOleStatus; export;
  554. var
  555.   SelfPtr: POleDocument;
  556. begin
  557.   SelfPtr := PAppServerDoc(Doc)^.Owner;
  558.  
  559.   if SelfPtr^.DocType <> DoctypeFromFile then
  560.     Save := Ole_Error_Generic
  561.   else
  562.   begin
  563.     SelfPtr^.SaveDoc;
  564.     Save := Ole_Ok;
  565.   end;
  566. end;
  567.  
  568. { Handles the Close callback.  We have been requested to close the document
  569.   because the client that contains a link (embedding or linking) to that 
  570.   document has shut down.  This is always called *before* the document's
  571.   'Release' callback is called.
  572.  
  573.   WHAT TO DO:
  574.     - Call OleRevokeServerDoc and *don't* free any resources until
  575.       'Release' is called
  576.     - Return the value of OleRevokeServerDoc
  577. }
  578. function Close(Doc: POleServerDoc): TOleStatus; export;
  579. var
  580.   SelfPtr: POleDocument;
  581. begin
  582.   SelfPtr:= PAppServerDoc(Doc)^.Owner;
  583.  
  584.   Close := OleRevokeServerDoc(SelfPtr^.ServerDoc);
  585. end;
  586.  
  587. { Responds to the SetHostNames callback.  The server library is calling
  588.   to provide the server with the name of the client's document and the
  589.   name of the object in the client application.  These names should be
  590.   used to make the necessary window title bar and menu changes.
  591.  
  592.   This is only called for embedded objects because linked objects display
  593.   their filename in the title bar.
  594.  
  595.    WHAT IT DOES:
  596.     - Change the title bar and File menu
  597.     - Store the object and client names for later use
  598.     - Return Ole_Ok is successful, Ole_Error_Generic otherwise
  599.  
  600.    PARAMETERS:
  601.     - 'Client' is the name of the client application document
  602.     - 'Doc' is the name of the object in the client application
  603. }
  604. function SetHostNames(Doc: POleServerDoc; Client,
  605.   DocName: PChar): TOleStatus; export;
  606. var
  607.   SelfPtr: POleDocument;
  608.   Title  : array [0..63] of Char;
  609. begin
  610.   SelfPtr := PAppServerDoc(Doc)^.Owner;
  611.   PServerWindow(Application^.MainWindow)^.UpdateFileMenu(DocName);
  612.  
  613.   { Store the document name, but don't update the title bar; we will do that
  614.     below
  615.   }
  616.   SelfPtr^.SetDocumentName(DocName, True);
  617.  
  618.   { Set the caption to be <App Name> - <Object Name> in <Client App Document>
  619.   }
  620.   StrCopy(Title, Application^.Name);
  621.   StrCat (Title, ' - ');
  622.   StrCat (Title, DocName);
  623.   StrCat (Title, ' in ');
  624.   StrCat (Title, Client);
  625.   PWindow(Application^.MainWindow)^.SetCaption(Title);
  626.  
  627.   SetHostNames := Ole_Ok;
  628. end;
  629.  
  630. { Handles the DocSetDimensions callback. The client is informing us how
  631.   big the object should be. 'Rect' is in mm_HiMetric units (all OLE
  632.   libraries express the size of every object in mm_HiMetric).  This
  633.   function is not supported.
  634. }
  635. function SetDocDimensions(Doc: POleServerDoc;
  636.   var Bounds: TRect): TOleStatus; export;
  637. begin
  638.   SetDocDimensions := Ole_Ok;
  639. end;
  640.  
  641. { Handles the GetObject callback. The server library calls this method
  642.   whenever a client application creates an object using a function like
  643.   OleCreate.  If 'ObjName' is nil, that means we are being called for an
  644.   embedded object after the server was sent 'Create', 'Edit', or
  645.   'CreateFromTemplate' and the server library requests the entire document.
  646.  
  647.   If 'ObjName' isn't nil then the server has already received a 'Open'
  648.   message to activate the linked object
  649.  
  650.   WHAT TO DO:
  651.     - Allocate a TOleObject if 'Item' is nil, or look up 'Item'
  652.       in the list of objects if it isn't nil
  653.     - Store the pointer to the TOleObject in 'OleObject' for return
  654.     - Store 'Client' so we can send notifications to the client
  655.       (used for linked objects)
  656.     - Return ole_Ok if successful, ole_Error_Name if 'Item' isn't
  657.       recognized, or ole_Error_Memory if the object could not be
  658.       allocated
  659.  
  660.   NOTE:
  661.     - We only have one object and it is created when the document is
  662.       created. Therefore, we don't actually create anything here.
  663.     - 'Client' resides in the server library and is used on behalf of
  664.       a client application
  665. }
  666. function GetObject(Doc: POleServerDoc; Item: PChar;
  667.   var OleObject: POleObject; Client: POleClient): TOleStatus; export;
  668. var
  669.   SelfPtr: POleDocument;
  670. begin
  671.   SelfPtr := PAppServerDoc(Doc)^.Owner;
  672.  
  673.   { In either case (whether 'ObjName' is nil or not) we just return
  674.     the object associated with the document.  NOTE that we return a
  675.     pointer to its AppObject field, not to the object itself.
  676.   }
  677.   OleObject := POleObject(@SelfPtr^.OleObject^.AppObject);
  678.  
  679.   { If 'Item' isn't nil then we associate 'Client' with it.
  680.   
  681.     NOTE: We only have one object. if you have multiple objects then you
  682.           would have to search your objects to find the one that matched
  683.           'Item'
  684.   }
  685.   if Item <> nil then
  686.     SelfPtr^.OleObject^.AddClientLink(Client);
  687.  
  688.   GetObject := Ole_Ok;
  689. end;
  690.  
  691. { Handles the Release callback.  The server library calls this routine when
  692.   all conversations to the object have been closed.  At this point the server
  693.   has called either OleRevokeServerDoc or OleRevokeServer.
  694.  
  695.   There will be no more calls to the document's methods.  It is thus okay to
  696.   free the document's objects, but *not* the TOleDocument yet.
  697.  
  698.   WHAT TO DO:
  699.     - Free the document's objects and resources (e.g. atoms) but *not* the
  700.       document itself
  701.     - Set a flag to indicate that 'Release' has been called
  702.     - Return Ole_Ok if successful, Ole_Error_Generic otherwise
  703.  
  704.   NOTE:
  705.     - Since we only have one document and one object within the
  706.       document we don't delete the object here.  However, you
  707.       might want to.
  708.     - This procedure is not called 'Release' because it appears in the
  709.       same scope as the Release callback for the TOleServerObj.
  710. }
  711. function ReleaseDoc(Doc: POleServerDoc): TOleStatus; export;
  712. var
  713.   SelfPtr: POleDocument;
  714. begin
  715.   SelfPtr := PAppServerDoc(Doc)^.Owner;
  716.  
  717.   SelfPtr^.IsReleased := True;
  718.   ReleaseDoc := Ole_Ok;
  719. end;
  720.  
  721. { Handles the SetColorScheme callback.  Not supported.
  722. }
  723. function SetColorSchemeDoc(Doc: POleServerDoc; var Palette: TLogPalette): TOleStatus; export;
  724. begin
  725.   SetColorSchemeDoc := Ole_Error_Generic;
  726. end;
  727.  
  728. { Handles the Execute callback.  If your app supports DDE execution commands
  729.   then you would handle this event.  Since we don't, we return
  730.   Ole_Error_Command.
  731. }
  732. function ExecuteDoc(Doc: POleServerDoc;
  733.   Commands: THandle): TOleStatus; export;
  734. begin
  735.   ExecuteDoc := ole_Error_Command;
  736. end;
  737.  
  738.  
  739. { TOleDocument Methods }
  740.  
  741. { Constructs an instance of the OLE Document. If 'Path' is nil then we
  742.   create an untitled document and default object.  The type is 'DoctypeNew'
  743.   if 'ServerDoc' is nil and 'DoctypeEmbedded' if 'ServerDoc' is non-nil.
  744.   If 'Path' is non-nil we create a document of type 'DoctypeFromFile'
  745.   and initialize it from file 'Path'
  746.     
  747.   If 'ServerDoc' is nil then we call OleRegisterServerDoc, otherwise we
  748.   just use 'ServerDoc' as our registration handle.
  749. }
  750. constructor TOleDocument.Init(Server: POleServerObj; Doc: LHServerDoc; 
  751.                               Path: PChar; Dirty: Boolean);
  752. begin
  753.   Name      := nil;
  754.   IsReleased:= False;
  755.   IsDirty   := Dirty;
  756.  
  757.   AppServerDoc.OleServerDoc.lpvtbl:= @OleServerDocVTbl;
  758.   AppServerDoc.Owner              := @Self;
  759.  
  760.   { Attach this document to the owning server.
  761.   }
  762.   POleServerObj(Server)^.Document := @Self;
  763.  
  764.   { Since we only have one object we can create it now.
  765.   }
  766.   OleObject := New(POleObjectObj, Init);
  767.  
  768.   if Path <> nil then
  769.     LoadFromFile(Path)
  770.   else
  771.   begin
  772.     SetDocumentName(UnnamedDoc, True);
  773.  
  774.     if Doc <> 0 then
  775.       DocType := DoctypeEmbedded
  776.     else
  777.       DocType := DoctypeNew;
  778.   end;
  779.  
  780.   if Doc <> 0 then
  781.     ServerDoc := Doc  { Use registration handle we were given }
  782.   else
  783.     OleRegisterServerDoc(Server^.ServerHdl, Name, @AppServerDoc, ServerDoc);
  784. end;
  785.  
  786. { Changes the instance variable 'Name' and changes the window caption to
  787.   those given.
  788. }
  789. procedure TOleDocument.SetDocumentName(NewName: PChar;
  790.   ChangeCaption: Boolean);
  791. var
  792.   Title: array[0..63] of Char;
  793. begin
  794.   StrDispose(Name);
  795.   Name := StrNew(NewName);
  796.  
  797.   if ChangeCaption then
  798.   begin
  799.     StrCopy(Title, Application^.Name);
  800.     StrCat (Title, ' - ');
  801.     StrCat (Title, NewName);
  802.     PWindow(Application^.MainWindow)^.SetCaption(Title);
  803.   end;
  804. end;
  805.  
  806. { Loads from the given file name.  Returns True if successful and False
  807.   otherwise.  If successful sets DocType to 'DoctypeFromFile' and sets
  808.   'Name' to 'Path'.
  809. }
  810. function TOleDocument.LoadFromFile(Path: PChar): Boolean;
  811. var
  812.   Msg     : array [0..255] of Char;
  813.   Key     : array [0..40]  of Char;
  814.   InStream: TBufStream;
  815. begin
  816.   InStream.Init(Path, stOpen, 1000);
  817.   if InStream.Status = stInitError then
  818.   begin
  819.     StrCopy(Msg, 'Cannot open file ');
  820.     StrCat(Msg, Path);
  821.     MessageBeep(0);
  822.     MessageBox(Application^.MainWindow^.HWindow, Msg,
  823.                Application^.Name, mb_OK or mb_IconExclamation);
  824.     LoadFromFile := False;
  825.   end
  826.   else
  827.   begin
  828.     { Read in the signature.  Read the number of characters we
  829.       would expect, then see if we got them.  If not, then abandon
  830.       the attempt.  Note that the Read will not get in a NUL; we
  831.       put that on manually.  Also note that we read StrLen(ClassKey)+1
  832.       characters to consume the extra blank written out.
  833.     }
  834.     InStream.Read(Key, StrLen(ClassKey)+1);
  835.     Key[StrLen(ClassKey)] := #0;
  836.     if StrComp(Key, ClassKey) <> 0 then
  837.     begin
  838.       StrCopy(Msg, 'File ');
  839.       StrCat(Msg, Path);
  840.       StrCat(Msg, ' is not an "');
  841.       StrCat(Msg, Application^.Name);
  842.       StrCat(Msg, '" file!');
  843.       MessageBeep(0);
  844.       MessageBox(Application^.MainWindow^.HWindow, Msg, Application^.Name,
  845.                  mb_OK or mb_IconExclamation);
  846.       LoadFromFile := False;
  847.     end
  848.     else
  849.     begin
  850.       OleObject:= POleObjectObj(InStream.Get);
  851.       DocType  := DoctypeFromFile;
  852.       SetDocumentName(Path, True);
  853.       LoadFromFile := True;
  854.     end;
  855.   end;
  856.   InStream.Done;
  857. end;
  858.  
  859. { Resets the document so that we can re-use the document object.  If your
  860.   app doesn't then you would delete the old object and create a new one.
  861.   Sets 'IsDirty' flag to False and 'IsReleased' to False. If 'ServerDoc'
  862.   is nil then calls OleRegisterServerDoc.
  863. }
  864. procedure TOleDocument.Reset(Path: PChar);
  865. begin
  866.   IsDirty    := False;
  867.   IsReleased := False;
  868.  
  869.   if Path <> nil then
  870.     if not LoadFromFile(Path) then 
  871.     begin
  872.       PServerWindow(Application^.MainWindow)^.ShapeChange(ObjEllipse);
  873.  
  874.       OleObject^.Native.NativeType := ObjEllipse;
  875.       OleObject^.Native.Version    := 1;
  876.  
  877.       DocType := DoctypeNew;
  878.       SetDocumentName(UnnamedDoc, True);
  879.     end;
  880.  
  881.   if ServerDoc = 0 then
  882.     OleRegisterServerDoc(POleApp(Application)^.Server^.ServerHdl, Name,
  883.       @AppServerDoc, ServerDoc);
  884. end;
  885.  
  886. { Sets up a TOpenFileName structure for use with the File Open Common
  887.   Dialog.  The caller passes in a structure which is filled in as
  888.   required, and a pointer to the array to receive the full path name.
  889.   Uses the Filter and SimpleName variables defined above, which are
  890.   global to allow this to be used from several places.
  891. }
  892. procedure TOleDocument.Setup(Path: PChar; MaxPathLen: Integer;
  893.   var FNStruct: TOpenFileName);
  894. begin
  895. { Set up a filter buffer to look for '*.oos' files only.  Recall that filter
  896.   buffer is a set of string pairs, with the last one terminated by a
  897.   double-null.
  898. }
  899.   FillChar(Filter, SizeOf(Filter), #0);  { Set up for double null at end }
  900.   StrCopy(Filter, 'OWL OLE Server');
  901.   StrCopy(@Filter[StrLen(Filter)+1], '*.oos');
  902.  
  903.   StrCopy(Path, '*.');
  904.   StrCat (Path, FileExt);
  905.  
  906.   FillChar(FNStruct, SizeOf(TOpenFileName), #0);
  907.  
  908.   with FNStruct do
  909.   begin
  910.     hInstance     := HInstance;
  911.     hwndOwner     := Application^.MainWindow^.HWindow;
  912.     lpstrDefExt   := FileExt;
  913.     lpstrFile     := Path;
  914.     lpstrFilter   := Filter;
  915.     lpstrFileTitle:= SimpleName;
  916.     Flags         := ofn_HideReadOnly or ofn_PathMustExist;
  917.     lStructSize   := SizeOf(TOpenFileName);
  918.     nFilterIndex  := 1;       {Use first Filter String in lpstrFilter}
  919.     nMaxFile      := MaxPathLen;
  920.   end;
  921. end;
  922.  
  923. { Activates the File/Open common dialog, and returns the result.
  924.   Puts the obtained file name into the given Path parameter, which
  925.   is assumed to point to a buffer big enough to contain a TFilename
  926.   sized string.
  927. }
  928. function TOleDocument.PromptForOpenFileName(Path: PChar): Boolean;
  929. var
  930.   FNStruct: TOpenFileName;
  931. begin
  932.   Setup(Path, SizeOf(TFilename), FNStruct);
  933.   PromptForOpenFileName := GetOpenFileName(FNStruct);
  934. end;
  935.  
  936. { Calls the common Windows dialog function to prompt the user for the
  937.   filename to use.
  938. }
  939. procedure TOleDocument.SaveAs;
  940. var
  941.   Path    : TFilename;    { Result of GetSaveFileName }
  942.   FNStruct: TOpenFileName;
  943. begin
  944.   Setup(Path, SizeOf(Path), FNStruct);
  945.  
  946.   if GetSaveFileName(FNStruct) then
  947.   begin
  948.     DocType := DoctypeFromFile;
  949.     SetDocumentName(Path, True);  { We must do this BEFORE we call SaveDoc }
  950.     SaveDoc;
  951.  
  952.     { Now inform the server library that we have renamed the document
  953.     }
  954.     OleRenameServerDoc(ServerDoc, Name);
  955.   end;
  956. end;
  957.  
  958. { Saves the document to file 'Name' and marks the document as no
  959.   longer 'dirty'.
  960. }
  961. procedure TOleDocument.SaveDoc;
  962. var
  963.   OutStream: TBufStream;
  964.   Blank    : Char;
  965. begin
  966.   if DocType = DoctypeNew then
  967.     SaveAs
  968.   else
  969.   begin
  970.     OutStream.Init(Name, stCreate, 1000);
  971.     OutStream.Write(ClassKey^, StrLen(ClassKey));
  972.     Blank := ' ';
  973.     OutStream.Write(Blank, 1);
  974.     OutStream.Put(OleObject);
  975.     IsDirty := False;
  976.     OutStream.Done;
  977.   end;
  978. end;
  979.  
  980. { Creates thunks for TOleServerDoc method callback tables
  981. }
  982. function TOleDocument_InitVTbl(Inst: THandle): Boolean;
  983. begin
  984.   @OleServerDocVTbl.Save            := MakeProcInstance(@Save,              Inst);
  985.   @OleServerDocVTbl.Close           := MakeProcInstance(@Close,             Inst);
  986.   @OleServerDocVTbl.SetHostNames    := MakeProcInstance(@SetHostNames,      Inst);
  987.   @OleServerDocVTbl.SetDocDimensions:= MakeProcInstance(@SetDocDimensions,  Inst);
  988.   @OleServerDocVTbl.GetObject       := MakeProcInstance(@GetObject,         Inst);
  989.   @OleServerDocVTbl.Release         := MakeProcInstance(@ReleaseDoc,        Inst);
  990.   @OleServerDocVTbl.SetColorScheme  := MakeProcInstance(@SetColorSchemeDoc, Inst);
  991.   @OleServerDocVTbl.Execute         := MakeProcInstance(@ExecuteDoc,        Inst);
  992.  
  993.   TOleDocument_InitVTbl := (@OleServerDocVTbl.Save <> nil) and
  994.                            (@OleServerDocVTbl.Close <> nil) and
  995.                            (@OleServerDocVTbl.SetHostNames <> nil) and
  996.                            (@OleServerDocVTbl.SetDocDimensions <> nil) and
  997.                            (@OleServerDocVTbl.GetObject <> nil) and
  998.                            (@OleServerDocVTbl.Release <> nil) and
  999.                            (@OleServerDocVTbl.SetColorScheme <> nil) and
  1000.                            (@OleServerDocVTbl.Execute <> nil);
  1001. end;
  1002.  
  1003. end.
  1004.